﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MMessaging.MessagingEx.Common;
using TUtils.BasicHelper.Logging;
using TUtils.BasicHelper;

namespace MMessaging.MessagingEx.Services
{
	/// <summary>
	/// Verbindet mehrere bidirektionale Channel (IMChannelEndDuplex) miteinander.
	/// Dabei wird ein Broadcastverfahren angewendet: Alle Messages werden an alle
	/// Channel weitergeitet, nur nicht an den, der die Message lieferte.
	/// </summary>
	/// <typeparam name="MESSAGETYPE"></typeparam>
	public class MServiceHubBroadcastDuplex<MESSAGETYPE> : IMService<MESSAGETYPE>, IMNodeDuplex<MESSAGETYPE>
		where MESSAGETYPE : class
	{
		#region private 

		private List<IMChannelEndDuplex<MESSAGETYPE>> _channels = new List<IMChannelEndDuplex<MESSAGETYPE>>();
		private List<MServiceHubBroadcast<MESSAGETYPE>> _broadcastThreads = new List<MServiceHubBroadcast<MESSAGETYPE>>();
		public string Name { get; private set; }
		private int threadNb = 0;
		private bool started = false;

		public void AddDuplexChannelEnd(IMChannelEndDuplex<MESSAGETYPE> channelEnd)
		{
			if (started)
				throw new ApplicationException("gsdhjcv732jskdvcb2w7h");

			var newBroadcastThread = new MServiceHubBroadcast<MESSAGETYPE>().Init(
				name:Name + "_" + threadNb.ToString(),
				inputs:new IMChannelEndOut<MESSAGETYPE>[] { channelEnd },
				outputs:new IMChannelEndIn<MESSAGETYPE>[] { },
				startAtOnce:false
				);

			foreach (var channel in _channels)
				newBroadcastThread.AddToOutputs(channel);

			foreach (var broadcastThread in _broadcastThreads)
				broadcastThread.AddToOutputs(channelEnd);

			_broadcastThreads.Add(newBroadcastThread);
			_channels.Add(channelEnd);

			if (_broadcastThreads.Count >= 2)
				_broadcastThreads[0].Join(newBroadcastThread);

			threadNb++;
		}

		#endregion

		public string ServiceName
		{
			get
			{
				return this.Name;
			}
		}


		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="name"></param>
		public MServiceHubBroadcastDuplex(string name)
		{
			Name = name;
			
		}

		/// <summary>
		/// Initialisiert den Hub vollständig.
		/// </summary>
		/// <param name="startAtOnce"></param>
		/// <param name="logXImplementor">
		/// if null, it will use the default application LogXImplementor (see LogXImplementor.dll)
		/// </param>
		/// <param name="channels"></param>
		/// <returns></returns>
		public MServiceHubBroadcastDuplex<MESSAGETYPE> Init(
			bool startAtOnce,
			params IMChannelEndDuplex<MESSAGETYPE>[] channels)
		{
			foreach (var channel in channels)
				AddDuplexChannelEnd(channel);

			if (startAtOnce)
				(this as IMService<MESSAGETYPE>).StartAsync();

			return this;
		}

		/// <summary>
		/// Startet Hub asynchron.
		/// Nochmaliger Aufruf eines schon gestarteten Hubs wird ohne Fehler ignoriert.
		/// </summary>
		public void StartAsync()
		{
			started = true;
			foreach (var broadcastThread in _broadcastThreads)
				broadcastThread.StartAsync();
		}

		/// <summary>
		/// Stoppt den Hub asynchron.
		/// </summary>
		public void CancelAsync()
		{
			foreach (var broadcastThread in _broadcastThreads)
				broadcastThread.CancelAsync();
		}

		public void Dispose()
		{
			(this as IMService<MESSAGETYPE>).CancelAsync();
		}
	}
}
